/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ruter.map;

/**
 * Streets are made of many PathSection connected, the first node (PathSection) has previous in null and in the last the next is pointing to null.
 * The objects of this class must be created by a map instance.
 * @author Camilo Ortegon
 */
public class PathSection implements MapComponent {

	public double x, y, lanWidth;
	public int leftLans, rigthLans;
	public double[][] lanPoints, arrivingLanPoints;
	public double[][] borders, arrivingBorder;
	public PathSection previous, next;
	public PathIntersection junction;

	/**
	 * PathSection initialized with the position and configuration, if is the head of the path previous must be set as null.
	 * @param x Position in x axis
	 * @param y Position in y axis
	 * @param laneWidth 
	 * @param leftLanes
	 * @param rigthLanes
	 * @param previous The constructing pathSection that goes before
	 */
	public PathSection(double x, double y, double laneWidth, int leftLanes, int rigthLanes, PathSection previous) throws MapException {

		if(leftLanes < 0 || rigthLanes < 0)
			throw new MapException("leftLanes and rigthLanes paramethers must be 0 or positive integer");

		this.x = x;
		this.y = y;
		this.lanWidth = laneWidth;
		this.leftLans = leftLanes;
		this.rigthLans = rigthLanes;
		this.previous = previous;
	}

	/**
	 * This method is used to join 2 PahtSection if they are closer than the minimalAproach
	 */
	public boolean joinPath(PathSection pathToJoin, double minimalAproach) {

		// Checking if it is not already in my intersection zone
		if(junction != null) {
			for (int i = 0; i < junction.conectedPaths.size(); i++) {
				if(pathToJoin == junction.conectedPaths.get(i))
					return false;
			}
		}

		// Checking if it's close to me
		if(Math.sqrt(Math.pow(x-pathToJoin.x, 2) + Math.pow(x-pathToJoin.x, 2)) < minimalAproach) {
			if(pathToJoin.junction == null) {
				pathToJoin.junction = new PathIntersection();
				pathToJoin.junction.conectedPaths.add(pathToJoin);
			}
			junction = pathToJoin.junction;
			junction.conectedPaths.add(this);
			this.x = pathToJoin.x;
			this.y = pathToJoin.y;

			return true;
		}

		return false;
	}

	/**
	 * This method is used to calculate the drawing points
	 */
	public void calculateDrawingPoints(boolean propagate) {

		if((previous != null || next != null)) {

			double angle = 0;
			if(previous != null) {
				if(next != null)
					angle = Geometry.angleBetweenTwoPoints(0, 0, (previous.x-x) + (x-next.x), (previous.y-y) + (y-next.y));
				else
					angle = Geometry.angleBetweenTwoPoints(x, y, previous.x, previous.y);
			} else if(next != null)
				angle = Geometry.angleBetweenTwoPoints(next.x, next.y, x, y);
			angle = Geometry.checkAngle(angle - (Math.PI/2));

			// Esto complica mi existencia
			//            int leftLans = this.leftLans, rigthLans = this.rigthLans;
			//            if(previous != null) {
			//                leftDifference = leftLans - previous.leftLans;
			//                rightDifference = rigthLans - previous.rigthLans;
			//                if(previous.leftLans > leftLans)
			//                    leftLans = previous.leftLans;
			//                if(previous.rigthLans > rigthLans)
			//                    rigthLans = previous.rigthLans;
			//            }

			if(previous != null && (previous.leftLans > 0 || previous.rigthLans > 0)) {
				arrivingLanPoints = new double[previous.leftLans+previous.rigthLans][2];
				int pos = 0;
				for (int i = previous.leftLans; i > 0; i--) {
					arrivingLanPoints[pos][0] = x+lanWidth*(i)*Math.cos(angle);
					arrivingLanPoints[pos][1] = y+lanWidth*(i)*Math.sin(angle);
					pos ++;
				}
				for (int i = 0; i < previous.rigthLans; i++) {
					arrivingLanPoints[pos][0] = x-lanWidth*(i+1)*Math.cos(angle);
					arrivingLanPoints[pos][1] = y-lanWidth*(i+1)*Math.sin(angle);
					pos ++;
				}
			}

			if(next != null && (leftLans > 0 || rigthLans > 0)) {
				lanPoints = new double[leftLans+rigthLans][2];
				int pos = 0;
				for (int i = leftLans; i > 0; i--) {
					lanPoints[pos][0] = x+lanWidth*(i)*Math.cos(angle);
					lanPoints[pos][1] = y+lanWidth*(i)*Math.sin(angle);
					pos ++;
				}
				for (int i = 0; i < rigthLans; i++) {
					lanPoints[pos][0] = x-lanWidth*(i+1)*Math.cos(angle);
					lanPoints[pos][1] = y-lanWidth*(i+1)*Math.sin(angle);
					pos ++;
				}
			}

			//            angulo = Math.toDegrees(angle);
			//            if(previous != null && next != null)
			//                System.out.println("--> Angulo: "+angulo);

			if(previous != null) {
				arrivingBorder = new double[2][2];
				arrivingBorder[0][0] = x+lanWidth*(previous.leftLans+0.5)*Math.cos(angle);
				arrivingBorder[0][1] = y+lanWidth*(previous.leftLans+0.5)*Math.sin(angle);
				arrivingBorder[1][0] = x-lanWidth*(previous.rigthLans+0.5)*Math.cos(angle);
				arrivingBorder[1][1] = y-lanWidth*(previous.rigthLans+0.5)*Math.sin(angle);
			}

			if(next != null) {
				borders = new double[2][2];
				borders[0][0] = x+lanWidth*(leftLans+0.5)*Math.cos(angle);
				borders[0][1] = y+lanWidth*(leftLans+0.5)*Math.sin(angle);
				borders[1][0] = x-lanWidth*(rigthLans+0.5)*Math.cos(angle);
				borders[1][1] = y-lanWidth*(rigthLans+0.5)*Math.sin(angle);
			}

			//            System.out.println("Punto izquierdo -> ("+borders[0][0]+","+borders[0][1]+")");
			//            System.out.println("Punto derecho -> ("+borders[1][0]+","+borders[1][1]+")");

		}

		if(propagate) {
			if(previous != null)
				previous.calculateDrawingPoints(false);
			if(next != null)
				next.calculateDrawingPoints(false);
			if(junction != null)
				junction.calculateDrawingPoints();
		}
	}

	@Override
	public double getX() {
		return x;
	}

	@Override
	public double getY() {
		return y;
	}

}
